package cn.song.logread.exception;

import cn.hutool.core.util.StrUtil;
import cn.song.logread.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;

import java.io.BufferedReader;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.json.JSONUtil.toJsonStr;

/**
 * 全局异常处理
 *
 * @author SongRenShuo
 * @since 2024-12-04 16:30:44
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class CommonAbnormal {



    /**
     * 异常处理程序
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> handlerHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String msg = "请求方式错误";
        logError(e, request, msg);
        return R.failed(msg + "：" + e.getMessage());
    }


    /**
     * 异常处理程序
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R<String> handlerHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
        String msg = "请求类型错误";
        logError(e, request, msg);
        return R.failed(msg + "：" + e.getMessage());
    }

    /**
     * 异常处理程序
     */
    // @ExceptionHandler(NonTransientDataAccessException.class)
    // public R<String> handlerSqlException(NonTransientDataAccessException e, HttpServletRequest request) {
    //     String msg = "";
    //     Throwable cause = e.getCause();
    //     if (cause instanceof SQLIntegrityConstraintViolationException) {
    //         msg = "数据插入失败,可能违反了完整性约束";
    //     } else if (cause instanceof SQLSyntaxErrorException) {
    //         msg = "SQL语句语法错误";
    //     }
    //     logError(e, request, "数据库操作错误：" + msg);
    //     return R.failed("数据库错误,请重试,超过三次失败,请尽快联系管理员：" + msg);
    // }

    /**
     * 异常处理程序
     */
    @ExceptionHandler(BindException.class)
    public R<String> handlerBindException(BindException e, HttpServletRequest request) {

        List<String> errorMessageList = e.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        String msg = "请求参数格式有误：" + errorMessageList;
        logError(e, request, msg);
        return R.failed(msg);
    }


    /**
     * 异常处理程序
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R<String> handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String msg = "请求参数类型不匹配";
        // 如果是HttpMessageNotReadableException，也可能是由于日期转换失败导致的
        if (e.getCause() instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException nestedException = (MethodArgumentTypeMismatchException) e.getCause();
            msg += "（嵌套）：无法将值 " + nestedException.getValue() + " 转换为类型 " + Objects.requireNonNull(nestedException.getRequiredType()).getSimpleName();
            logError(e, request, msg);
            return R.failed(msg);
        }
        msg += "：无法将值 " + e.getValue() + " 转换为类型 " + Objects.requireNonNull(e.getRequiredType()).getSimpleName();
        logError(e, request, msg);
        return R.failed(msg);

    }

    /**
     * 异常处理程序
     */
    // @ExceptionHandler(ConstraintViolationException.class)
    // public R<String> handlerConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
    //     // 校验框架 - violation
    //     String msg = "请求参数错误：";
    //     StringBuilder errorMessage = new StringBuilder(msg);
    //     Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
    //     for (ConstraintViolation<?> violation : violations) {
    //         String messageTemplate = violation.getMessageTemplate();
    //         // // 大多数情况下错误信息会包含冒号分隔的字段名和错误描述
    //         if (messageTemplate.contains(":")) {
    //             errorMessage.append(violation.getMessage().split(":")[1].trim()).append(", ");
    //         } else {
    //             errorMessage.append(violation.getMessage()).append(" ");
    //         }
    //     }
    //     // 去除最后一个逗号和空格
    //     errorMessage.setLength(errorMessage.length());
    //     logError(e, request, msg + errorMessage);
    //     return R.failed(errorMessage.toString());
    // }

    /**
     * 异常处理程序
     */
    @ExceptionHandler(Throwable.class)
    public R<String> handlerException(Throwable e, HttpServletRequest request) {
        String msg = "全局异常处理异常捕获";
        logError(e, request, msg);
        String message = StrUtil.length(e.getMessage()) > 200 ? "系统异常,请尽快联系管理员" : e.getCause() == null ? e.getMessage() : e.getCause().getMessage();
        return R.failed("操作失败：" + message);
    }

    /**
     * 错误日志打印
     *
     * @param e       异常信息
     * @param request 请求
     * @param msg     描述
     */
    private static void logError(Throwable e, HttpServletRequest request, String msg) {
        HashMap<String, Object> formData = getAllFormDataParameters(request);
        String body = getAllBodyParameters(request);
        log.error("{} 请求路径：{} formData参数：{} body参数：{} 错误原因：{}", msg, request.getRequestURI(), toJsonStr(formData), body, e.getMessage(), e);
    }


    /**
     * 获取request中所有formData参数
     *
     * @param request 请求
     * @return {@link HashMap }<{@link String }, {@link Object }>
     */
    public static HashMap<String, Object> getAllFormDataParameters(HttpServletRequest request) {
        HashMap<String, Object> formData = new HashMap<>(16);
        try {
            // 获取所有参数名
            Enumeration<String> e = request.getParameterNames();
            // 通过Enumeration类中的hasMoreElements()判断是否还有参数名
            while (e.hasMoreElements()) {
                // 获取当前参数名
                String parameterName = e.nextElement();
                // 再通过request.getParameter("")的方法来获取对应参数名的值
                formData.put(parameterName, request.getParameter(parameterName));
            }
        } catch (Exception e) {
            log.error("获取request中所有formData参数 异常：{}", e.getMessage());
        }
        return formData;
    }


    /**
     * 获取request中所有Body参数
     *
     * @param request 请求
     * @return {@link String }
     */
    public static String getAllBodyParameters(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try {
            try (BufferedReader br = request.getReader()) {
                String line;
                while ((line = br.readLine()) != null) {
                    // 添加换行符，保持原始格式
                    sb.append(line).append('\n');
                }
            }
        } catch (Exception e) {
            log.error("获取request中所有Body参数 异常：{}", e.getMessage());
        }
        return sb.toString();
    }

}


